package ru.svi.bluetooth;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class BluetoothService {
	private static final String[] NAMES = { "WS", "TP", "RH", "BP" };
	// Debugging
	private static final String TAG = "BluetoothService";

	// Unique UUID for this application
	private static final UUID GREBANIY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

	// Member fields
	private final BluetoothAdapter mAdapter;
	private final Handler mHandler;
	private ConnectThread mConnectThread;
	private RecieveThread mRecieveThread;
	private BluetoothSocket mSocket = null;
	private int mState;

	// Constants that indicate the current connection state
	public static final int STATE_NONE = 0; // we're doing nothing
	public static final int STATE_CONNECTING = 1; // now initiating an outgoing
													// connection
	public static final int STATE_CONNECTED = 2; // now connected to a remote
													// device

	public BluetoothService(Context context, Handler handler) {
		mAdapter = BluetoothAdapter.getDefaultAdapter();
		mState = STATE_NONE;
		mHandler = handler;
	}

	private synchronized void setState(int state) {
		mState = state;
	}

	public synchronized int getState() {
		return mState;
	}

	public synchronized void receiveData(BluetoothDevice device) {
		if (mConnectThread != null) {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		if (mRecieveThread != null)
			mRecieveThread = null;

		if (mSocket != null)
			try {
				mSocket.close();
				mSocket = null;
			} catch (IOException e) {
				e.printStackTrace();
			}

		mConnectThread = new ConnectThread(device);
		mConnectThread.start();
		setState(STATE_CONNECTING);
	}

	public void recieveAgain() {
		mRecieveThread = new RecieveThread(mSocket);
		mRecieveThread.start();
	}

	private synchronized void recieve(BluetoothDevice device) {
		if (mRecieveThread != null)
			mRecieveThread = null;

		mRecieveThread = new RecieveThread(mSocket);
		mRecieveThread.start();

		if (mConnectThread != null)
			mConnectThread = null;
	}

	public synchronized void stop() {
		if (mConnectThread != null) {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		if (mRecieveThread != null) {
			mRecieveThread = null;
		}

		if (mSocket != null)
			try {
				mSocket.close();
				mSocket = null;
			} catch (IOException e) {
				e.printStackTrace();
			}

		setState(STATE_NONE);
	}

	private void connectionFailed(BluetoothDevice device) {
		setState(STATE_NONE);
		Message msg = mHandler
				.obtainMessage(BlueToothThingActivity.MESSAGE_CONNECTION_FAILED);
		Bundle bundle = new Bundle();
		bundle.putParcelable(BlueToothThingActivity.DEVICE, device);
		msg.setData(bundle);
		mHandler.sendMessage(msg);
	}

	private void connectionLost(BluetoothDevice device) {
		try {
			if (mSocket != null) {
				mSocket.close();
				mSocket = null;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		mSocket = null;
		setState(STATE_NONE);

		Message msg = mHandler
				.obtainMessage(BlueToothThingActivity.MESSAGE_CONNECTION_LOST);
		Bundle bundle = new Bundle();
		bundle.putParcelable(BlueToothThingActivity.DEVICE, device);
		msg.setData(bundle);
		mHandler.sendMessage(msg);
	}

	private class ConnectThread extends Thread {
		private BluetoothSocket mmSocket;
		private BluetoothDevice mmDevice;

		public ConnectThread(BluetoothDevice device) {
			setName("ConnectThreadSecured");
			mmDevice = device;
			BluetoothSocket tmp = null;

			try {
				tmp = device.createRfcommSocketToServiceRecord(GREBANIY_UUID);
			} catch (IOException e) {
				Log.e(TAG, "Socket create() failed", e);
			}
			mmSocket = tmp;
		}

		public void run() {
			// Always cancel discovery because it will slow down a connection
			mAdapter.cancelDiscovery();

			try {
				mmSocket.connect();
			} catch (IOException e) {
				try {
					mmSocket.close();
					mmSocket = null;
				} catch (IOException e2) {
				}
				connectionFailed(mmDevice);
				return;
			}

			setState(STATE_CONNECTED);

			// Send the name of the connected device back to the UI Activity
			Message msg = mHandler
					.obtainMessage(BlueToothThingActivity.MESSAGE_CONNECTED);
			Bundle bundle = new Bundle();
			bundle.putString(BlueToothThingActivity.DEVICE_NAME,
					mmDevice.getName());
			msg.setData(bundle);
			mHandler.sendMessage(msg);

			mSocket = mmSocket;
			recieve(mmDevice);
		}

		public void cancel() {
			if (mmSocket != null)
				try {
					mmSocket.close();
				} catch (IOException e) {
					Log.e(TAG, "close() of connect socket failed", e);
				}
		}
	}

	private class RecieveThread extends Thread {
		private final InputStream mmInStream;
		private final OutputStream mmOutStream;
		private BluetoothDevice device;

		public RecieveThread(BluetoothSocket socket) {
			InputStream tmpIn = null;
			OutputStream tmpOut = null;
			device = socket.getRemoteDevice();
			try {
				tmpIn = socket.getInputStream();
				tmpOut = socket.getOutputStream();
			} catch (IOException e) {
				Log.e(TAG, "temp sockets not created", e);
			}

			mmInStream = tmpIn;
			mmOutStream = tmpOut;
		}

		public void run() {
			try {
				String curValuesMessage = getMessageString((byte) 'S');
				MessageParser parser = new MessageParser(curValuesMessage);
				// Send the obtained bytes to the UI Activity
				mHandler.obtainMessage(BlueToothThingActivity.MESSAGE_READ,
						parser.getMeasures(NAMES)).sendToTarget();
			} catch (IOException e) {
				Log.e(TAG, "disconnected", e);
				connectionLost(device);
			}
		}

		private String getMessageString(byte mode) throws IOException {
			byte[] buffer = new byte[256];
			int bytes = 0;
			mmOutStream.write(new byte[] { mode, '\r' });
			mmOutStream.flush();
			String readMessage = "";
			while (!readMessage.equals("\r\n> ") && bytes < buffer.length) {
				bytes += mmInStream.read(buffer, bytes, buffer.length - bytes);
				if (bytes < 5)
					continue;
				readMessage = new String(buffer, bytes - 4, 4);
			}

			return new String(buffer, 0, bytes);
		}
	}

}
